package com.game.common.cloud;

import io.netty.util.concurrent.DefaultEventExecutor;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Promise;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

@Service
public class PlayerServiceInstance implements ApplicationListener<GameChannelCloseEvent> {
    /**
     * 缓存PlayerId对应的所有服务实例id
     * Long  playerId
     * Integer serviceId
     * Integer serverId
     */
    private Map<Long, Map<Integer, Integer>> serviceInstanceMap = new ConcurrentHashMap<>();
    private BusinessServerService businessServerService;
    private StringRedisTemplate  stringRedisTemplate;
    // 创建一个事件线程，操作redis
    private EventExecutor eventExecutor = new DefaultEventExecutor();
    @Autowired
    public PlayerServiceInstance(BusinessServerService businessServerService, StringRedisTemplate stringRedisTemplate) {
        this.businessServerService = businessServerService;
        this.stringRedisTemplate = stringRedisTemplate;
    }

    public Set<Integer> getAllServiceId() {
        return businessServerService.getAllServiceId();
    }

    /**
     * 根据playerId和serviceId获取服务实例id
     * @param playerId
     * @param serviceId
     * @param promise
     * @return
     */
    public Promise<Integer> selectServerId(Long playerId, Integer serviceId, Promise<Integer> promise) {
        // 获取Map serviceId 对应的serverId
        Map<Integer, Integer> instanceMap = this.serviceInstanceMap.get(playerId);
        Integer serverId = null;
        if (!CollectionUtils.isEmpty(instanceMap)) {
            instanceMap = new ConcurrentHashMap<>();
            this.serviceInstanceMap.put(playerId, instanceMap);
        } else {
            serverId = instanceMap.get(serviceId);
        }
        if (serverId != null) {
            if (businessServerService.isEnableServer(serviceId, serverId)) {
                promise.setSuccess(serverId);
            } else {
                serverId = null;
            }
        }
        if (serverId == null) {
            eventExecutor.execute(()->{
                try{
                    String key = this.getRedisKey(playerId);
                    Object value = stringRedisTemplate.opsForHash().get(key, String.valueOf(serviceId));
                    boolean flag = true;
                    if (value != null) {
                        int serverIdOfRedis = Integer.parseInt(value.toString());
                        flag = businessServerService.isEnableServer(serviceId, serverIdOfRedis);
                        if (flag) {
                            promise.setSuccess(serverIdOfRedis);
                            this.addLocalCache(playerId, serviceId, serverIdOfRedis);
                        }
                    }
                    if (value == null || !flag) {
                        Integer serverId2 = this.selectServerIdAndSaveRedis(playerId, serviceId);
                        this.addLocalCache(playerId, serviceId, serverId2);
                        promise.setSuccess(serverId2);
                    }
                }catch(Throwable e) {
                    promise.setFailure(e);
                }
            });
        }
        return promise;
    }
    @Override
    public void onApplicationEvent(GameChannelCloseEvent event) {
        this.serviceInstanceMap.remove(event.getPlayerId());
    }

    /**
     * 添加本地缓存
     * @param playerId
     * @param serviceId
     * @param serverId
     */
    private void addLocalCache(long playerId, int serviceId, int serverId) {
        Map<Integer, Integer> instanceMap = this.serviceInstanceMap.get(playerId);
        instanceMap.put(serviceId, serverId);
    }

    /**
     * 生成redis key
     * @param playerId
     * @return
     */
    private String getRedisKey(Long playerId) {
        return "service_instance_" + playerId;
    }

    /**
     * 获取服务实例id，并保存到redis中
     * @param playerId
     * @param serviceId
     * @return
     */
    private Integer selectServerIdAndSaveRedis(Long playerId, Integer serviceId) {
        Integer serverId = this.businessServerService.selectServerInfo(serviceId, playerId).getServerId();
        this.eventExecutor.execute(() -> {
            try {
                String key = this.getRedisKey(playerId);
                this.stringRedisTemplate.opsForHash()
                        .put(
                                key,
                                String.valueOf(serviceId),
                                String.valueOf(serverId)
                        );
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        return serverId;
    }
}
